#!/bin/env perl

#
# Bitch.
#

use strict;
use warnings;

#
# Dependencies.
#

use Getopt::Long;
use List::MoreUtils qw( all );
use MIME::Lite;
use JSON;
use LWP::UserAgent;
use Try::Tiny;

#
# Options.
#

my @required = qw( repository transaction kind hash host port look smtp from root );

my %option;

GetOptions ( \%option, qw{
  repository=s
  transaction=s
  
  kind=s
  hash=s
  
  host=s
  port=s
  
  look=s
  
  smtp=s
  from=s
  root=s
} ) and ( all { defined $option{$_} } @required )
  or die 'Usage: isock_svn_pre_commit_hook --repository=$path --transaction=$transaction --kind=<datasheet|specification> --hash=$hash --host=$host --port=$port--look=$look --smtp=$smtp --from=$from --root=$root';

#
# Variables.
#

my $repository  = $option{repository};
my $transaction = $option{transaction};

my $kind        = $option{kind};
my $hash        = $option{hash};

my $host        = $option{host};
my $port        = $option{port};

my $look        = $option{look};

my $smtp        = $option{smtp};
my $from        = $option{from};
my $root        = $option{root};

my $svn_tail    = "--transaction $transaction $repository";

#
# Info.
#

my ( $looked, $author, $timestamp, $size, $message );

{
  open my $fh, "$look info $svn_tail |" or die 'could not invoke svnlook info';
  
  while ( <$fh> ) {
    $looked   .= "  - $_";
    
    $author    = $_, next unless $author;
    $timestamp = $_, next unless $timestamp;
    $size      = $_, next unless $size;
    
    $message  .= $_  if $author && $timestamp && $size;
  }

  chomp $author;
  chomp $timestamp;
  chomp $size;
  chomp $message;
}

#
# Bitch.
#

unless ( $message ) {
  die 'commit message shall not be empty';
}

#
# Changed.
#

my @changed;

{
  open my $fh, "$look changed $svn_tail |" or die 'could not invoke svnlook changed';
  
  @changed = <$fh>;
}

my @projects;

{
  my $project;
     $project->{$_}++ for map { m/^ [A-Z] \s+ ([^\/]+) /x ; $1 } grep { not m/^_/ } @changed;
  
  @projects = sort keys %$project;
}

#
# Core.
#

my @errors;

for my $project ( @projects ) {
  try {
    my $creation = scalar grep { m/^ A \s+ $project \/ $/x } @changed;
    
    authorized ( $author, 'tinkerer', $project, $option{kind}, $creation );
  }
  catch {
    ( my $why = $_ ) =~ s/ at \/.+\.$//;
    chomp $why;
    
    push @errors, $why;
  };
}

if ( @errors ) {
  my $mime = MIME::Lite->new (
    From    => $from,
    To      => $root,
    Subject => "[subversion] pre-commit $kind failed (" . join ( ", ", @projects ) . ")",
    Type    => 'TEXT',
    Data    =>
      "Author  : $author\n"
    . "Command : $look changed $svn_tail\n"
    . "Looked  :\n$looked"
    . "Changed :\n" . join ( '', map { "  - $_"   } @changed )
    . "Errors  :\n" . join ( '', map { "  - $_\n" } @errors  )
    ,
  );

  my $sent_ok = $mime->send ( smtp => $smtp );
  
  die "unauthorized commit for transaction $transaction (e-mail " . ( $sent_ok ? "" : "NOT " ) . "sent to administrator)";
}

exit 0;

#
# Subs.
#

sub authorized {
  my ( $user_id, $authorization_id, $whatever_id, $whatever, $creation ) = @_;
  
  if ( my $ua = LWP::UserAgent->new ) {
    $ua->no_proxy ( $host );
    $ua->timeout ( 10 * 60 );
    
    if ( my $request = HTTP::Request->new ( GET => "http://$host:$port/rest/user/$user_id/authorization/$authorization_id/$whatever/$whatever_id?hash=$hash" . ( $creation ? "&creation=1" : "" ) ) ) {
      $request->content_type ( 'application/json' );

      if ( my $response = $ua->request ( $request ) ) {
        my $content;
        
        try {
          $content = decode_json ( $response->content );
        };

        if ( $response->is_success ) {
          return $content->{authorization};
        }
        else {
          die $content->{error} || $response->status_line;
        }
      }
      else {
        die 'Could not get any response!';
      }
    }
    else {
      die 'Could not build request!';
    }
  }
  
  die 'Could not build user agent!';
}
